package erfgame.core.world.terrain.file;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;


import erfgame.core.Point3D;
import erfgame.core.world.World;
import erfgame.core.world.terrain.BlockGridTiles;
import erfgame.core.world.terrain.GridTile;
import erfgame.core.world.terrain.WalkablePath;

public class BlockGridTilesSerializer implements Serializer<BlockGridTiles> {

	private World world;
	
	public BlockGridTilesSerializer( World world ) {
		this.world = world;
	}
	
	public BlockGridTiles retrieveObject(InputStream ins, BlockGridTiles into)
			throws Exception {
		int xgrids = world.getXGridTilesPerBlock();
		int ygrids = world.getYGridTilesPerBlock();
		int xpaths = world.getXPathTilesPerBlock();
		int ypaths = world.getYPathTilesPerBlock();
		
		DataInputStream dis = new DataInputStream( ins );
		
		int bx = dis.readInt();
		int by = dis.readInt();
		
		int wx = bx * world.getBlockWidth();
		int wy = by * world.getBlockHeight();
		
		GridTile[][][] gridTiles = new GridTile[xgrids][ygrids][];
		
		for( int x=xgrids; x>0; ) {
			x--;
			GridTile[][] gridTilesX = gridTiles[x];
			for( int y=ygrids; y>0; ) {
				y--;
				int length = dis.read();
				GridTile[] gridTilesXY = new GridTile[length];
				for( int i=length; i>0; ) {
					i--;
					int z = dis.readShort();
					GridTile tile = new GridTile(z);
					gridTilesXY[i] = tile;
					int[][] pathIndexes = new int[BlockGridTiles.MAX_INDEX+1][];
					for( int j=BlockGridTiles.MAX_INDEX; j>=0; j-- ) {
						int pathIndexesLength = dis.read();
						int[] pathIndexesJ = new int[pathIndexesLength];
						for( int k=pathIndexesLength; k>0; ) {
							k--;
							pathIndexesJ[k] = dis.read();
						}
						pathIndexes[j] = pathIndexesJ;
					}
					tile.setPathIndexes(pathIndexes);
				}
				gridTilesX[y] = gridTilesXY;
			}
		}
		
		WalkablePath[][][] paths;
		int available = dis.read();
		if( available == 0 ) {
			paths = null; 
		} else {
			paths = new WalkablePath[xpaths][ypaths][];
			
			for( int x=xpaths; x>0; ) {
				x--;
				WalkablePath[][] pathsX = paths[x];
				for( int y=ypaths; y>0; ) {
					y--;
					int length = dis.read();
					WalkablePath[] pathsXY = new WalkablePath[length];
					for( int i=length; i>0; ) {
						i--;
						int fromGridIndex = dis.read();
						int toGridIndex = dis.read();
						int maxStepUp = dis.readShort();
						int maxStepDown = dis.readShort();
						int minHeight = dis.readShort();
						int minWidth = dis.readShort();
						int weight = dis.read();
						byte moveType = (byte)dis.read();
						
						int pointsLength = dis.read();
						ArrayList<Point3D> points = new ArrayList<Point3D>(pointsLength);
						while( pointsLength > 0 ) {
							int px = dis.read() + wx;
							int py = dis.read() + wy;
							int pz = dis.readShort(); 
							Point3D point = new Point3D(px, py, pz);
							points.add( point );
							pointsLength--;
						}
						
						WalkablePath path = new WalkablePath(
								moveType, 
								points, 
								minHeight, 
								minWidth, 
								weight, 
								maxStepUp, 
								maxStepDown, 
								fromGridIndex, 
								toGridIndex
						);
						pathsXY[i] = path;
					}
					pathsX[y] = pathsXY;
				}
			}
		}
		return new BlockGridTiles(bx, by, gridTiles, paths);
	}

	public void storeObject(OutputStream outs, BlockGridTiles object)
			throws Exception {
		int xgrids = world.getXGridTilesPerBlock();
		int ygrids = world.getYGridTilesPerBlock();
		int xpaths = world.getXPathTilesPerBlock();
		int ypaths = world.getYPathTilesPerBlock();

		DataOutputStream dos = new DataOutputStream( outs );
		
		int bx = object.getBx();
		int by = object.getBy();
		
		int wx = bx * world.getBlockWidth();
		int wy = by * world.getBlockHeight();
		
		dos.writeInt( bx );
		dos.writeInt( by );
		
		GridTile[][][] gridTiles = object.getGridTiles();
		for( int x=xgrids; x>0; ) {
			x--;
			GridTile[][] gridTilesX = gridTiles[x];
			for( int y=ygrids; y>0; ) {
				y--;
				GridTile[] gridTilesXY = gridTilesX[y];
				dos.write( gridTilesXY.length ); 
				for( int i=gridTilesXY.length; i>0; ) {
					i--;
					GridTile gridTile = gridTilesXY[i];
					dos.writeShort( gridTile.getZ() );
					int[][] pathIndexes = gridTile.getPathIndexes();
					for( int j=pathIndexes.length; j>0; ) {
						j--;
						int[] pathIndexesJ = pathIndexes[j];
						if( pathIndexesJ != null ) {
							dos.write( pathIndexesJ.length ); 
							for( int k=pathIndexesJ.length; k>0; ) {
								k--;
								dos.write( pathIndexesJ[k] );
							}
						} else {
							dos.write( 0 );
						}
					}
				}
			}
		}
		
		// XY offset here to better compress px, py
		
		WalkablePath[][][] paths = object.getPaths();
		if( paths == null ) {
			dos.write( 0 );
		} else {
			dos.write( 1 );
			for( int x=xpaths; x>0; ) {
				x--;
				WalkablePath[][] pathsX = paths[x];
				for( int y=ypaths; y>0; ) {
					y--;
					WalkablePath[] pathsXY = pathsX[y];
					dos.write( pathsXY.length );
					for( int i=pathsXY.length; i>0; ) {
						i--;
						WalkablePath path = pathsXY[i];
						dos.write( path.getFromGridIndex() );
						dos.write( path.getToGridIndex() );
						dos.writeShort( path.getMaxStepUp() );
						dos.writeShort( path.getMaxStepDown() );
						dos.writeShort( path.getMinHeight() );
						dos.writeShort( path.getMinWidth() );
						dos.write( path.getWeight() );
						dos.write( path.getMoveType() );
						List<Point3D> points = path.getPoints();
						// probably don't actually need to write the points length - can be derived from world
						dos.write( points.size() );
						for( int k=0; k<points.size(); k++ ) {
							Point3D p = points.get( k );
							dos.write( p.x - wx );
							dos.write( p.y - wy );
							dos.writeShort( p.z );
						}
					}
				}
			}
		}		
		dos.flush();
	}
}
